package org.as3search.config {
	/*
	 * as3search - http://code.google.com/p/as3search
	 * Copyright (c) 2009-2010 by respective authors. All rights reserved.
	 *
	 * This library is free software; you can redistribute it and/or modify it under the
	 * terms of the GNU Lesser General Public License as published by the Free Software
	 * Foundation; either version 2.1 of the License, or (at your option) any later
	 * version.
	 *
	 * This library is distributed in the hope that it will be useful, but WITHOUT ANY
	 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
	 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
	 *
	 * You should have received a copy of the GNU Lesser General Public License along
	 * with this library; if not, write to the Free Software Foundation, Inc.,
	 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
	 */
	
	import org.as3search.criteria.Criteria;
	import org.as3search.criteria.impl.CriteriaImpl;
	import org.as3search.properties.DefaultPropertyResolver;
	import org.as3search.properties.PropertyResolver;
	import org.as3search.providers.SearchProvider;
	import org.as3search.providers.SearchProviderMapping;
	import org.as3search.providers.impl.SearchProviderMappingImpl;
	
	/**
	 * <code>AbstractSearchConfiguration</code> serves as a default implementation of a
	 * <code>SearchConfiguration</code>. It does not define any <code>SearchProvider</code>s
	 * or a <code>failoverProvider</code>, but does define a default <code>propertyResolver</code>.
	 * 
	 * @see org.as3search.config.SearchConfiguration
	 * @see org.as3search.providers.SearchProvider
	 * @see org.as3search.properties.PropertyResolver
	 */
	public class AbstractSearchConfiguration implements SearchConfiguration {
		
		private var _providers:Array = [];
		
		private var _propertyResolver:PropertyResolver = new DefaultPropertyResolver();
		
		private var _failoverProvider:SearchProviderMapping;
		/**
		 * Creates a new <code>AbstractSearchConfiguration</code> instance.
		 */
		public function AbstractSearchConfiguration() {
			
		}
		/**
		 * Creates a new <code>Criteria</code> implementation by which to search through
		 * <code>value</code>. Override this method in a subclass to provide your own 
		 * <code>Criteria</code> implementation. 
		 * 
		 * @param	value	The value to search through.
		 * @return A new <code>Criteria</code> instance for searching through <code>value</code>. 
		 * 		By default, <code>AbstractSearchConfiguration.createCriteria()</code> returns a 
		 * 		<code>org.as3search.criteria.impl.CriteriaImpl</code> instance.
		 * 
		 * @see org.as3search.criteria.Criteria
		 * @see org.as3search.criteria.impl.CriteriaImpl
		 */
		public function createCriteria(value:Object):Criteria {
			return new CriteriaImpl(getSearchProvider(value), this);
		}
		/**
		 * Returns an appropriate <code>SearchProvider</code> for <code>value</code>.
		 * <code>AbstractSearchConfiguration</code> loops through all of its stored
		 * <code>SearchProviderMapping</code> instances, and returns the first appropriate 
		 * provider. If no suitable provider can be found, <code>failoverProvider</code>
		 * is used.
		 * 
		 * @param	value	The value to retrieve a <code>SearchProvider</code> for.
		 * @return Returns the best appropriate <code>SearchProvider</code> instance for <code>value</code>.
		 * 
		 * @see org.as3search.providers.SearchProvider
		 * @see org.as3search.providers.SearchProviderMapping
		 * @see org.as3search.providers.impl.SearchProviderMappingImpl
		 */
		public function getSearchProvider(value:Object):SearchProvider {
			for each (var providerMapping:SearchProviderMapping in providers) {
				if (value is (providerMapping.targetType))
					return providerMapping.create(value);
			}
			
			return failoverProvider.create(value);
		}
		/**
		 * Adds a <code>SearchProvider</code> mapping to this instance's stored 
		 * provider dictionary.
		 * 
		 * @param	targetType	The <code>Class</code> of the object to map to <code>providerType</code>.
		 * @param	providerType The <code>Class</code> of the <code>SearchProvider</code> to map to 
		 * 		provide instances for values of type <code>targetType</code>.
		 * @return This instance for method chaining.
		 * 
		 * @see org.as3search.providers.SearchProvider
		 * @see org.as3search.providers.SearchProviderMapping
		 */
		public function addSearchProvider(targetType:Class, providerType:Class):SearchConfiguration {
			providers.push(new SearchProviderMappingImpl(targetType, providerType));
			return this;
		}
		/**
		 * Removes a <code>SearchProvider</code> from this configurations provider
		 * dictionary.
		 * 
		 * @param	targetType	The <code>Class</code> key to the <code>SearchProvider</code>
		 * 		class to remove from this configuration.
		 * 
		 * @return Returns this instance for method chaining.
		 */
		public function removeSearchProvider(targetType:Class):SearchConfiguration {
			for each (var providerMapping:SearchProviderMapping in providers) {
				if (providerMapping.targetType == targetType) {
					providers.splice(providers.indexOf(providerMapping), 1);
					break;
				}
			}
			return this;
		}
		/**
		 * Stores all <code>SearchProviderMapping</code> objects which map target types
		 * to provider types for returning <code>SearchProvider</code> instances. Must
		 * be an <code>Array</code> of <code>SearchProviderMapping</code> instances.
		 * 
		 * @throws ArgumentError 'providers' must be set to an array of only implementors of
		 * 		org.as3search.providers.SearchProviderMapping
		 * 
		 * @see org.as3search.providers.SearchProvider
		 * @see org.as3search.providers.SearchProviderMapping
		 */
		public function get providers():Array { return _providers; }
		public function set providers(value:Array):void {
			if (value == null) {
				_providers = [];
				return;
			}
			
			for (var i:uint = 0; i < value.length; i++) {
				if (!(value[i] is SearchProviderMapping))
					throw new ArgumentError("'providers' must be set to an array of " +
						"only implementors of org.as3search.providers.SearchProviderMapping.");
			}
			
			_providers = value;
		}
		/**
		 * The failover provider for this <code>SearchConfiguration</code>. This <code>SearchProvider</code>
		 * is used when no appropriate mapping can be found for an object.
		 * 
		 * @see org.as3search.providers.SearchProviderMapping
		 */
		public function get failoverProvider():SearchProviderMapping { return _failoverProvider; }
		public function set failoverProvider(value:SearchProviderMapping):void {
			_failoverProvider = value;
		}
		/**
		 * The property resolver for this <code>SearchConfiguration</code>. This <code>PropertyResolver</code>
		 * instance is responsible for translating properties names into values.
		 * 
		 * @see org.as3search.properties.PropertyResolver
		 */
		public function get propertyResolver():PropertyResolver { return _propertyResolver; }
		public function set propertyResolver(value:PropertyResolver):void { _propertyResolver = value; }
	}
}